#define _CRT_SECURE_NO_WARNINGS 1




		《 进程创建 》

fork函数初识
在linux中fork函数时非常重要的函数，它从已存在进程中创建一个新进程。
新进程为: 子进程，而原进程为父进程。

#include <unistd.h>
pid_t fork(void);

返回值:【子进程中 返回0】，【父进程返回 [子进程id]，[出错返回 -1]】

为什么给 父进程返回的是 子进程的PID， 给子进程返回的是 0？
答：为了让父进程方便对子进程进行标识，进而进行管理(比如通过 PID杀掉子进程)

进程调用fork，当控制转移到内核中的fork代码后，内核做 :
	·分配新的内存块和内核数据结构给子进程
	·将父进程部分数据结构内容拷贝至子进程
	·添加子进程到系统进程列表当中			:Linux中所有进程都是被维护在一个双链表的结构中，进程的PCB 要列入到大OE的调度算法中，140的优先级队列中
	·fork返回，开始调度器调度

进程: 内核的相关管理数据结构：(task_struct(进程控制块) + mm_struct(进程地址空间) + 页表) + 代码(共享的) 和 数据(写时拷贝)

——————————————————————————————————————————————————————————

fork常规用法：
。一个父进程希望复制自己，使父子进程同时执行不同的代码段。例如，父进程等待客户端请求，生成子进程来处理请求。
。一个进程要执行一个不同的程序。例如子进程从fork返回后，调用exec函数。

fork调用失败的原因
·系统中有太多的进程
·实际用户的进程数超过了限制

——————————————————————————————————————————————————————————


——————————————————————————————————————————————————————————


		《 进程的终止 》


进程: 内核的相关管理数据结构：(task_struct(进程控制块) + mm_struct(进程地址空间) + 页表) + 代码(共享的) 和 数据(写时拷贝)


1．想明白:终止是在做什么 ?
    释放曾经的代码和数据所占据的空间
    释放内核数据结构				:pcb中 task_struct 会被延期处理：Z状态：僵尸进程


——————————————————————————————————————————————————————————


2. 进程终止的3种情况?
    a. 代码跑完，结果正确
    b. 代码跑完，结果不正确
    c. 代码执行时，出现了异常，提前退出了。

vs 编程运行的时候，崩溃了-- - 操作系统发现你的进程做了不该做的事情，0S杀了进程
一旦出现异常，退出码没有意义了!
我为什么出现了异常了﹖原因是﹖
进程出异常，本质是因为进程收到了OS发给进程的信号!
		< KILL -l: (11)SIGSEGV >
[a@192 f]$ kill - 11 22118
I am a process, pid: 22118
segmentation fault

我们可以看进程退出的时候,退出信号是多少，
就可以判断我的进程为什么异常了! ! ! 与错误码类似。

如何判断异常？
1．先确认是否是异常
2．不是异常，就一定是代码跑完了，看退出码就行。

衡量一个进程退出，我们只需要两个数字: 退出码, 退出信号!


——————————————————————————————————————————————————————————


		《 echo $?	》	 
查看程序结束返回值。

main()函数为什么一般返回0？ 
因为 main() 作为 bash的子进程 (父进程bash获取到的,最近一个子进程退出的退出码)
退出码 是为了告诉关心方(一般是父进程)，我任务完成的怎么样了
由退出码 通过strerror转字符串描述 用户可以明确的知道 退出的原因 (是成功或错误，以及错误的原因)

例如：C语言中： return 0;	return 100;
——————————————————————————————————————————————————————————

		< Makefile >
bin = myprocess
src = myprocess.c

$(bin) :$(src)
@gcc - o $@ $ ^
.PHONY:clean
clean :
@rm - f $(bin)

		< myprocess.c >
#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("I an process! pid: %d, ppid: %d\n", getpid(), getppid());
    sleep(2);

    return 0;
}

[a@192 f]$ ./myprocess
I an process!pid: 2945, ppid : 2553		//ppid的父进程： bash 命令行解释器，bash本身也是进程pid：2553
[a@192 f]$ echo $ ?
0

echo 是内建命令，打印的都是bash 内部的变量数据 man函数的返回值就是：'？' 
		
        < 进程的退出码 >
	? :这个符号是 父进程bash获取到的,最近一个子进程退出的退出码

0  : 成功√
!0 : 标识失败
!0 时：例如： 1, 2, 3.4，5, 6, 7....不同的非O值，
! 一方面表示失败，一方面表示失败的原因
都有对应的错误描述->string

——————————————————————————————————————————————————————————

		< Linux 打印错误信息 >

int main()
{
    for (int errcode = 0; errcode <= 255; errcode++)
    {
        printf("%d: %s\n", errcode, strerror(errcode));
    }
    return 0;
}

0 : success
1 : operation not permitted
2 : No such file or directory
3 : No such process
4 : Interrupted system call
5 : Input / output error
6 : No such device or address
7 : Argument list too long
8 : Exec format error
9 : Bad file descriptor
10 : No child processes

父进程 bash为什么要得到子进程的退出码呢﹖
要知道 子进程退出的 情况(成功，失败，失败的原因是什么?)
目的：让用户知道程序是否成功运行，或者为什么失败？

——————————————————————————————————————————————————————————

		< 自定义错误码 >

#include <stdio.h>
//#include <unistd.h>

enum
{
    Success = 0,
    Div_Zero,
    Mod_Zero,
};

int exit_code = Success;

const char* CodeToErrString(int code)
{
    switch (code)
    {
    case Success:
        return "Success!";
    case Div_Zero:
        return "Div_Zero!";
    case Mod_Zero:
        return "Mod_Zero!";
    default:
        return "Unknow error!";
    }
}

int Div(int x, int y)
{
    if (0 == y)
    {
        exit_code = Div_Zero;   //重新赋值错误码
        return -1;
    }
    else
        return x / y;
}

int main()
{
    int result = Div(10, 100);
    printf("result: %d [%s]\n", result, CodeToErrString(exit_code));
    result = Div(10, 0);
    printf("result: %d [%s]\n", result, CodeToErrString(exit_code));

    return exit_code;
}

[a@192 f]$ . / myprocess
result : 0 [Success!]
result : -1[Div_Zero!]
[a@192 f]$


——————————————————————————————————————————————————————————


3．《 如何终止 》 ?


a.main函数return, 表示进程终止（非main函数，return，函数结束)
b.代码调用exit函数注意:我们代码的任意位置调用exit，都表示进程退出
c._exito) --system call
exit vs _exit : exit会在进程退出的时候，充刷缓冲区, _exit不会
目前所说的缓冲区，不是内核缓冲区。
exit(C库函数) 会先冲刷缓冲区 关闭流等，再调用 _exit(操作系统).

【 ------------ 正常运行的程序 ------------ 】
       ↓                     ↓
       ↓           【执行用户定义的清理函数】
       ↓[ _exit ]            ↓[ exit ]
       ↓           【冲刷缓冲，关闭流等    】
       ↓                     ↓
【 ------------     kernel     ------------ 】


		< exit >
exit - cause normal process termination	//退出-导致正常进程终止
SYNOPSIS	(简介):
	#include <stdlib.h>
	void exit(int status);

衡量一个进程退出，我们只需要两个数字:退出码，退出信号!  


——————————————————————————————————————————————————————————


		《 进程等待 》

进程等待必要性
· 之前讲过，子进程退出，父进程如果不管不顾，就可能造成僵尸进程'的问题，进而造成内存泄漏。
· 另外，进程一旦变成僵尸状态，那就刀枪不入，“杀人不眨眼”的kill -9也无能为力，因为谁也没有办法杀死一个已经死去的进程。
· 最后，父进程派给子进程的任务完成的如何，我们需要知道。如，子进程运行完成，结果对还是不对，或者是否正常退出。
· 父进程通过进程等待的方式，回收子进程资源，获取子进程退出信息。  //要知道子进程是什么原因退出(可选功能)

		《 wait 》
进程等待的方法 ：
#include<sys/types.h>
#include<sys/wait.h>

pid_t wait(int* status);		
等待父进程中，任意子进程退出
		《 阻塞等待 》
如果子进程没有退出，父进程其实一直在进行阻塞等待！
子进程本身就是软件，父进程本质是在等待某种软件条件就绪，你如何理解阻塞等待子进程呢 ?
：父进程状态设为非运行状态，把父进程的PCB链接到子进程的队列当中，子进程退出时再唤醒父进程，期间就是阻塞等待。

返回值:
成功返回被等待进程pid，失败返回 - 1。
参数 :
	输出型参数，获取子进程退出状态, 不关心则可以设置成为NULL


——————————————————————————————————————————————————————————


			< 等待观察 Z僵尸进程 >

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

void childRun()
{
    int cnt = 5;
    while (cnt)
    {
        printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
        sleep(1);
        cnt--;
    }
}

int main()
{
    printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
    pid_t id = fork();
    if (id == 0)
    {
        // child
        childRun();
        printf("child quit ...\n");
        exit(0);
    }
    sleep(10);
    // fahter
    pid_t rid = wait(NULL);
    if (rid > 0)
    {
        printf("wait success,rid: %d\n", rid);
    }
    sleep(3);
    printf("father quit ... \n");
    return 0;
}

[a@192 g]$ . / myprocess
I am father, pid: 3650, ppid : 3245
I am child process, pid : 3651, ppid : 3650, cnt : 5
I am child process, pid : 3651, ppid : 3650, cnt : 4
I am child process, pid : 3651, ppid : 3650, cnt : 3
I am child process, pid : 3651, ppid : 3650, cnt : 2
I am child process, pid : 3651, ppid : 3650, cnt : 1
child quit ...				//子进程退出 Z僵尸
wait success, rid : 3651		//父进程等待
father quit ...				//父进程退出

[a@192 g]$ while :; do ps axj | head - 1 && ps axj | grep myprocess | grep - v grep; sleep 1; don
// ...						//子进程进入僵尸进程 父进程等待
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
3245   3650   3650   3245 pts / 1      3650 S + 1000   0:00 . / myprocess
3650   3651   3650   3245 pts / 1      3650 S + 1000   0 : 00 . / myprocess
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
3245   3650   3650   3245 pts / 1      3650 S + 1000   0 : 00 . / myprocess
3650   3651   3650   3245 pts / 1      3650 Z + 1000   0 : 00[myprocess] <defunct>
// ...

父进程退出 
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
3245   3650   3650   3245 pts / 1      3650 S + 1000   0:00 . / myprocess
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND


——————————————————————————————————————————————————————————


			< waitpid >

pid_t wait(int* status);		
pid_t waitpid(pid_t pid, int* status, int options);
返回值:
当正常返回的时候 waitpid 返回收集到的 子进程 的进程ID;
如果设置了选项 WNOHANG, 而调用中 waitpid 发现没有已退出的子进程可收集, 则返回0;
如果调用中出错, 则返回 - 1, 这时 errno 会被设置成相应的值以指示错误所在;
参数:
pid:
Pid = -1, 等待任一个子进程。与 wait 等效。
Pid > 0 , 等待其进程 ID 与 pid 相等的子进程。
例如：
pid_t rid = wait(NULL);      ==↓
pid_t waitpid(-1, NULL, 0);  ==↑
pid_t waitpid(id, NULL, 0);  等待指定 id 

——————————————————————————————————————————————————————————

pid_t waitpid(pid_t pid, int* status, int options);

		< status >

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

void childRun()
{
    int cnt = 5;
    while (cnt)
    {
        printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
        sleep(1);
        cnt--;
    }
}

int main()
{
    printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
    pid_t id = fork();
    if (id == 0)
    {
        // child
        childRun();
        printf("child quit ...\n");
        exit(1);
    }
    sleep(10);
    // fahter
    //pid_t rid = wait(NULL);
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0)
    {
        printf("wait success,rid: %d\n", rid);
    }
    else
    {
        printf("wait failed!\n");
    }
    sleep(3);
    printf("father quit ... status: %d\n", status);
    printf("father quit ... status: %d, child quit code : %d, child quit signal: %d\n", status, (status>>8)&0xff, status&0x7f); 

    return 0;
}

I am child process, pid: 4784, ppid : 4783, cnt : 1
child quit ...
wait success, rid : 4784
father quit ... status : 256

子进程退出信息： 进程退出码 + 退出信号！

2. 进程终止的3种情况?
a, 代码跑完，结果正确
b.代码跑完，结果不正确
c． 代码执行时，出现了异常，提前退出了。

int status ==> 【[0000 0000 0000 0000] [0000 0000 0000 0000]】
只使用后 16 个比特位
正常终止  ：   【[---- ---- ---- ----] [0000 0000 ---- ----]】  正常终止只使用 16 位中的前 8位    255种，足够表示不同的信号
被信号所杀：   【[---- ---- ---- ----] [---- ---- -000 0000]】  终止信号只使用 16 位中的后 7位    128种
core dump ：   【[---- ---- ---- ----] [---- ---- 0--- ----]】  最后一位为：core dump 标志        


(status >> 8) & 0xff	//右移 8 去掉后八位，只获取 正常终止的 8比特位。
status & 0x7ff)			//获取 被信号所杀的： 7比特位。
father quit ... status: 256, child quit code : 1, child quit signal : 0

死循环子进程：
kill -9 3650		//主动杀掉 子进程
father quit ... status: 256, child quit code : 1, child quit signal : 9

异常：
void childRun()
{
		int *p = NULL;
    while (1)
    {
        printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
        sleep(1);
		  *p = 100;		//对空指针解引用。
    }
}
father quit ... status: 256, child quit code : 1, child quit signal : 11
 11) SIGSEGV  :段错误通常是由于程序访问了不属于它的内存空间, 对空指针解引用。

256 ==【[---- ---- ---- ----][---- ---1 0000 0000]】

——————————————————————————————————————————————————————————


——————————————————————————————————————————————————————————
